﻿/********************************************************************************/
/* 	player.d																	*/
/*------------------------------------------------------------------------------*/
/*	製作		（　゜ワ゜）ノ　／　松久貫太									*/
/*	製作開始　	2007/05/11														*/
/*	MAIL		omega@personal.email.ne.jp										*/
/*	URL		http://nagoya.cool.ne.jp/o_mega										*/
/*																				*/
/*	このソースは「やわらかライセンス」の元で配布されています。					*/
/*-更新履歴---------------------------------------------------------------------*/
/*	2007/--/--																	*/
/*-その他-----------------------------------------------------------------------*/
/*	なし																		*/
/********************************************************************************/

/* ライブラリのインポート *******************************************************/
private import hell2;		// dHell2ライブラリ
private import actor;
private import vector;
private import screen;
private import keypad;
private import state;
private import shot;
private import effect;
private import enemy;

private import std.string;
private import std.math;
private import std.math2;
private import std.stream;

private import defines;

/**/

class PlayerActor : ActorPool!(Player)
{
public:
	GameMain gamemain;
	
	this(GameMain gm,int n)
	{
		gamemain = gm;
		super(n);
	}
	
	void add(PlayerSpec ps)
	{
		Player p = this.getInstance();
		if(p)
		{
			p.init(gamemain,ps);
		}
	}
	
	Player getNearest(Vec3 v)
	{
		float dist = 99999999.0;
		Player result = null;
		
		foreach(Player p;actors)
		{
			if(p.exist)
			{
				float d = p.position.getDistSqrt(v);
				if(d < dist && p.damage_timer < 1)
				{
					dist = d;
					result = p;
				}
			}
		}
		return result;
	}
	
	void separate(){
		foreach(Player p;actors){
			if(p.exist == false) continue;
			
			foreach(Player p2;actors){
				if(p2.exist == false || p == p2) continue;
				
				Vec3 dist = p.position - p2.position;
				if(dist.getDist() < p.getSize() * 2){
					if(p.playerspec.mode == PlayerSpec.BASE){
						p2.position -= dist.getIdentity() * 0.2;
					}else if(p2.playerspec.mode == PlayerSpec.BASE){
						p.position += dist.getIdentity() * 0.2;
					}else{
						p.position += dist.getIdentity() * 0.1;
						p2.position -= dist.getIdentity() * 0.1;
					}
				}
			}
		}
	}
	
	void drawPlayer()
	{
		foreach(Player a;actors)
		{
			if(a.exist)
			{
				a.drawPlayer();
			}
		}
	}
	void drawStatus()
	{
		foreach(Player a;actors)
		{
			if(a.exist)
			{
				a.drawStatus();
			}
		}
	}
	
	int count(){
		int res = 0;
		foreach(Player a;actors)
		{
			if(a.exist) res++;
		}
		return res;
	}
	
	void destroyAll(){
		foreach(Player a;actors){
			if(a.exist) a.destroy();
		}
	}
}


class PlayerSpec{
	enum{
		PLAYER,
		REPLAYER,
		BASE,
	}
	
	int mode;
	int life;
	float size;
	MouseRecorder mr;
	
	this(int mode , MouseRecorder mr){
		this.mode = mode;
		this.mr = mr;
		
		if(mode == BASE){
			life = 100;
			size = 3.0;
		}else{
			life = 0;
			size = 1.0;
		}
	}
}

class Player : Actor
{
private:
	static const float HIT_SIZE = 0.4;
	static const float SHOT_SPEED = 0.7;
	
	//static const float ROTATE_SPEED = 0.1;
	static const float TARLET_NOCKFORCE = 1.0;
	
	static const float ROTATE_SPEED = 0.06;
	static const float MOVE_SPEED = 0.08;
	
	static const int AMMO_MAX = 6;
	static const int AMMO_RELOAD_INTERVAL = 240;
	static const float SHOT_RANGE = CHAR_X_ABSMAX / 2;
	static const float LINK_RANGE = CHAR_X_ABSMAX / 3;
	
	static const int DAMAGE_WAIT = 120;
public:
	int timer;
	double direction;
	double tarlet;
	double tarlet_nockback;
	
	int damage;
	
	Vec3 marker;
	Vec3 cursor;
	int marker_timer;
	
	int ammo;
	int ammo_reload;
	int damage_timer;
	PlayerSpec playerspec;
	
	Player[] linked;
	
	enum STATE
	{
		NORMAL,
		ROTATE,
	}
	STATE state;
	
	GameMain gamemain;
	
	this()
	{
		super();
	}
	
	void init(GameMain gm,PlayerSpec ps){
		gamemain = gm;
		
		exist = true;
		position = new Vec3(0,0, CHAR_Z_SURFACE );
		marker = new Vec3(0,0, CHAR_Z_SURFACE );
		cursor = new Vec3(0,0, CHAR_Z_SURFACE );
		timer = 0;
		damage = 0;
		
		direction = -3.14159 / 2;
		tarlet = direction;
		state = STATE.NORMAL;
		marker_timer = 0;
		tarlet_nockback = 0;
		damage_timer = 0;
		
		ammo = AMMO_MAX;
		ammo_reload = 0;
		linked = new Player[gamemain.playeractor.getCount];
		foreach(inout pl;linked) pl = null;
		
		playerspec = ps;
		if(ps.mode == PlayerSpec.REPLAYER){
			position = new Vec3(Hell_randInt(-30,30) / 5.0 , Hell_randInt(-30,30) / 5.0 , CHAR_Z_SURFACE );
		}else if(ps.mode == PlayerSpec.BASE){
			position = new Vec3( 0 , CHAR_Y_ABSMAX - ps.size, CHAR_Z_SURFACE );
		}
	}
	
	void move()
	{
		if(!exist) return;
		
		// 入力更新
		if(playerspec.mr){
			playerspec.mr.update();
			cursor.x = (playerspec.mr.getX() - 320) / 320.0 * CHAR_X_ABSMAX;
			cursor.y = (playerspec.mr.getY() - 240) / 240.0 * CHAR_Y_ABSMAX;
		}
		
		if(damage_timer > 0){
			damage_timer--;
			return;
		}
		
		if(playerspec.mode == PlayerSpec.BASE){
			moveBase();
		}else{
			moveTank();
		}
	}
	
	void moveBase(){
		timer++;
		checkCollisionToEnemy();
	}
	
	void moveTank(){		
		timer++;
		// 移動座標
		if(playerspec.mr.isPress() & 0x04)
		{
			marker.x = cursor.x;
			marker.y = cursor.y;
		}
		
		// 砲塔まわり
		double tarlet_tg = atan2(cursor.y - position.y , cursor.x - position.x);
		tarlet = tarlet_tg;
		tarlet_nockback *= 0.5;
		
		moveByMouse();
		checkCollisionToEnemy();
		
		
		// 弾を出す
		if(gamemain.shotactor.countType(SHOTTYPE.NORMAL) < 8 && (playerspec.mr.isPush() & 0x01) && ammo > 0 && timer > 10)
		{
			ammo--;
			ammo_reload = 0;
			
			Vec3 tg = cursor - position;
			float r = tg.getDist();
			if(r > SHOT_RANGE) r = SHOT_RANGE;
			
			gamemain.shotactor.fire(
				position ,
				position + tg.getIdentity() * r ,
				SHOTTYPE.NORMAL
			);
			tarlet_nockback = TARLET_NOCKFORCE;
			Hell_playWAV("fire");
		}else{
			if(ammo < 1){
				ammo_reload++;
				foreach(pl;linked) if(pl) ammo_reload+=2;
				
				if(ammo_reload > AMMO_RELOAD_INTERVAL){
					ammo = AMMO_MAX;
					Hell_playWAV("reload");
				}
			}else{
				ammo_reload = 0;
			}
		}
		
		// リンク判定
		checkPlayerLink();
	}
	
	void checkPlayerLink(){
		foreach(inout Player pl;linked) pl = null;
		
		int idx = 0;
		foreach(inout Player pl;gamemain.playeractor.actors){
			if(pl.exist == false || pl == this) continue;
			
			Vec3 dist = pl.position - position;
			if(dist.getDist() < LINK_RANGE){
				linked[idx] = pl;
				idx++;
			}
		}
	}
	
	void moveByMouse()
	{
		Vec3 sp = new Vec3();
		sp.x = cursor.x - position.x;
		sp.y = cursor.y - position.y;
		
		if(position.x < -CHAR_X_ABSMAX + 2)	position.x = -CHAR_X_ABSMAX + 2;
		if(position.x >  CHAR_X_ABSMAX - 2)	position.x =  CHAR_X_ABSMAX - 2;
		if(position.y < -CHAR_Y_ABSMAX + 2)	position.y = -CHAR_Y_ABSMAX + 2;
		if(position.y >  CHAR_Y_ABSMAX - 2)	position.y =  CHAR_Y_ABSMAX - 2;
		
		// 移動する
		double dx = marker.x - position.x;
		double dy = marker.y - position.y;
		if(dx * dx + dy * dy > 1 * 1) {
			double move_tg = atan2(marker.y - position.y , marker.x - position.x);
			
			double PI = 3.141592;
			double move_r = move_tg - direction;
			while(move_r > PI) move_r -= PI * 2;
			while(move_r < -PI) move_r += PI * 2;
			
			if(fabs(move_r) > PI / 2){
				if(move_r > 0){
					direction -= PI;
					move_r -= PI;
				}else{
					direction += PI;
					move_r += PI;
				}
			}else{
				if(fabs(move_r) < ROTATE_SPEED){
					direction = move_tg;
				}else{
					if(move_r > 0){
						direction += ROTATE_SPEED;
					}else{
						direction -= ROTATE_SPEED;
					}
				}
				
				position.x += MOVE_SPEED * cos(direction);
				position.y += MOVE_SPEED * sin(direction);
			}
		}
	}
	
	void setMarker()
	{
		if(Hell_isPushMouse() & 0x01)
		{
			marker.x = cursor.x;
			marker.y = cursor.y;
		}
	}
	
	void checkCollisionToEnemy()
	{
		Enemy e = gamemain.enemyactor.getNearestEnemy(position);
		
		if(e){
			if( e.position.getDist(position) < getSize() + e.getSize())
			{
				// Destory
				e.destroy();
				//exist = false;
				damage++;
				damage_timer = DAMAGE_WAIT;
				for(int t=0;t<64;t++)
				{
					gamemain.addEffect(position , position , EFFECTTYPE.TRIANGLE);
				}
			}
		}
	}
	
	void drawPlayer(){
		if(playerspec.mode == PlayerSpec.BASE){
			drawBase();
		}else{
			drawTank();
		}
	}
	
	void drawBase(){
		Vec3 drawpos = new Vec3(position);
		Vec3 shake_rot = new Vec3(0,0,0);
		if(damage_timer > 0){
			drawpos.x += Hell_randInt(-10,10) / 40.0 / DAMAGE_WAIT * damage_timer;
			drawpos.y += Hell_randInt(-10,10) / 40.0 / DAMAGE_WAIT * damage_timer;
			drawpos.z += Hell_randInt(-10,10) / 40.0 / DAMAGE_WAIT * damage_timer;
			shake_rot.x = Hell_randInt(-10,10) * 5.0 / DAMAGE_WAIT * damage_timer;
			shake_rot.y = Hell_randInt(-10,10) * 5.0 / DAMAGE_WAIT * damage_timer;
			shake_rot.z = Hell_randInt(-10,10) * 5.0 / DAMAGE_WAIT * damage_timer;
		}
		
		glPushMatrix();
		{
			Hell_setAlpha(HELL_ALPHA_NORMAL);
			glTranslatef(drawpos.x , drawpos.y , drawpos.z);
			glRotatef(0,0,direction / 3.14159 * 180);
			glRotatef(shake_rot.y , shake_rot.z , shake_rot.x);
			glScalef(getSize() * 1.2 , getSize() * 1.2, 1);
			glColor4f(1 , 1 , 1, 0.5);
			CubeShape.drawPolygon();
			glColor4f(1 , 1 , 1, 0.5);
			CubeShape.drawWire();
		}
		glPopMatrix();
	}
	
	void drawTank(){
		if(!exist) return;
		
		float alpha = (playerspec.mode == PlayerSpec.PLAYER) ? 1.0 : 0.5 ;
		Vec3 drawpos = new Vec3(position);
		Vec3 shake_rot = new Vec3(0,0,0);
		if(damage_timer > 0){
			drawpos.x += Hell_randInt(-10,10) / 40.0 / DAMAGE_WAIT * damage_timer;
			drawpos.y += Hell_randInt(-10,10) / 40.0 / DAMAGE_WAIT * damage_timer;
			shake_rot.x = Hell_randInt(-10,10) * 5.0 / DAMAGE_WAIT * damage_timer;
			shake_rot.y = Hell_randInt(-10,10) * 5.0 / DAMAGE_WAIT * damage_timer;
			shake_rot.z = Hell_randInt(-10,10) * 5.0 / DAMAGE_WAIT * damage_timer;
		}
		
		glPushMatrix();
		if(playerspec.mode == PlayerSpec.PLAYER){
			Hell_setAlpha(HELL_ALPHA_NORMAL);
			glTranslatef(marker.x , marker.y , marker.z);
			glRotatef(0,0,timer*6);
			glScalef(2, 2 , 1);
			glColor4f(0 , 0.5 , 1, 0.5 * alpha);
			SquareShape.drawWire();
		}
		glPopMatrix();
		
		// 戦車
		glPushMatrix();
		{
			Hell_setAlpha(HELL_ALPHA_NORMAL);
			glTranslatef(drawpos.x , drawpos.y , drawpos.z);
			glRotatef(0,0,direction / 3.14159 * 180);
			glRotatef(shake_rot.y , shake_rot.z , shake_rot.x);
			glScalef(1,1,1);
			glColor4f(0.6 , 0.4 , 0.3 ,1 * alpha);
			TankShape.drawPolygonBase();
			if(playerspec.mode == PlayerSpec.PLAYER){
				glColor4f(1.0 , 0.8 , 0.6 , 1 * alpha);
			}else{
				glColor4f(0.3 , 0.2 , 0.1 , 1 * alpha);
			}
			TankShape.drawWireBase();
		}
		glPopMatrix();
		// 砲塔
		glPushMatrix();
		{
			Hell_setAlpha(HELL_ALPHA_NORMAL);
			glTranslatef(drawpos.x , drawpos.y , drawpos.z);
			glRotatef(0,0,tarlet / 3.14159 * 180);
			glRotatef(shake_rot.x , shake_rot.y , shake_rot.z);
			glTranslatef(-tarlet_nockback , 0 , 0);
			glScalef(1,1,1);
			glColor4f(0.6 , 0.4 , 0.3 ,1 * alpha);
			TankShape.drawPolygonTarlet();
			if(playerspec.mode == PlayerSpec.PLAYER){
				glColor4f(1.0 , 0.8 , 0.6 , 1 * alpha);
			}else{
				glColor4f(0.3 , 0.2 , 0.1 , 1 * alpha);
			}
			TankShape.drawWireTarlet();
		}
		glPopMatrix();
		
		alpha = (playerspec.mode == PlayerSpec.PLAYER) ? 1.0 : 0.2 ;
		
		// カーソル
		glPushMatrix();
		if(playerspec.mode == PlayerSpec.PLAYER){
			glTranslatef(cursor.x , cursor.y , cursor.z);
			glRotatef(timer * 1.5 , timer * 1.1 , timer * 1.7);
			glRotatef(shake_rot.y , shake_rot.z , shake_rot.x);
			glColor4f(1,1,1,1 * alpha);
			TriangleShape.drawWire();
			
			glRotatef(timer * 1.5 , timer * 1.1 , timer * 1.7);
			glRotatef(shake_rot.y , shake_rot.z , shake_rot.x);
			glColor4f(1,1,1,0.8 * alpha);
			glScalef(2,2,2);
			TriangleShape.drawWire();
		}
		glPopMatrix();
		
		drawLink();
	}
	
	void drawLink(){
		foreach(inout pl;linked){
			if(!pl) continue;
			
			glPushMatrix();
			Vec3 v1 = position + new Vec3(Hell_randInt(-16,16) / 32.0 , Hell_randInt(-16,16) / 32.0 , 0);
			Vec3 v2 = pl.position + new Vec3(Hell_randInt(-16,16) / 32.0 , Hell_randInt(-16,16) / 32.0 , 0);
			if(ammo > 0 || playerspec.mode != PlayerSpec.PLAYER){
				glColor4f(0.1 , 0.75 , 1.0 , 0.4);
				LineShape.drawWireThin(v1 , v2);
			}else{
				glColor4f(1.0 , 1.0 , 0.2 , 0.8);
				LineShape.drawWire(v1 , v2);
			}
			glPopMatrix();
		}
	}
	
	void drawStatus()
	{
		if(playerspec.mode == PlayerSpec.REPLAYER) return;
		
		if(playerspec.mode == PlayerSpec.PLAYER){
			if(ammo > 0){
				Hell_drawFont(std.string.toString(ammo),320 -8 ,240 -8 , 2 , 255,255,255,192);
			}else{
				if(timer % 6 < 3){
					Hell_drawFont("RELOAD",320 -8*6 -8 ,240 -8 , 2 , 255,160,64,255);
					Hell_drawFont(std.string.toString(AMMO_RELOAD_INTERVAL - ammo_reload) ,320 -8*6 -8 ,240 +8 , 2 , 255,160,64,255);
				}
			}
		}else if(playerspec.mode == PlayerSpec.BASE){
			Hell_drawFont("DAMAGE : " ~ std.string.toString(damage),0 ,0 , 2 , 255,255,255);
		}
	}
	
	float getSize(){
		return playerspec.size;
	}
	
	void destroy(){
		exist = false;
		for(int t=0;t<64;t++)
		{
			gamemain.addEffect(position , position , EFFECTTYPE.TRIANGLE);
		}
	}
}